/**
 * @PluginLoader.java  COPYRIGHT xiaojian
 * 
 * Created on 2019年7月10日 上午10:31:54
 */
package xj.toolkit.ebus.cl;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collection;
import java.util.List;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;

import com.google.common.collect.Lists;

import xj.toolkit.ebus.Constants;
import xj.toolkit.util.JVMUtil;
import xj.toolkit.util.PackageUtils;

/**
 * 加载指定插件目录下的插件，如果有新的，则重新生成一个classloader。
 * 
 * @author <a href="mailto:cxj2000@gmail.com">xiaojian.cao</a>
 *
 */
public class PluginLoader implements InitializingBean, DisposableBean {

	private static final Logger logger = LoggerFactory.getLogger(PluginLoader.class);

	private String pluginDirectory = null;

	public String getPluginDirectory() {
		return pluginDirectory;
	}

	public void setPluginDirectory(String plugDirectory) {
		this.pluginDirectory = plugDirectory;
	}

	/**
	 * 模块是否启用。
	 */
	private boolean enabled = false;

	public boolean isEnabled() {
		return enabled;
	}

	public void setEnabled(boolean enabled) {
		this.enabled = enabled;
	}

	@Autowired
	FileAlterationMonitor fileAlterationMonitor = null;

	@Override
	public void afterPropertiesSet() throws Exception {
		if (!enabled) {
			return;
		}

		System.setProperty(Constants.SYSCFG_PLUGIN_ENABLE, "true");

		this.reloadCl();

	}

	@Override
	public void destroy() throws Exception {
		if (!enabled) {
			return;
		}
	}

	private File calcPluginDirectory() {
		File plugDirFile = new File(pluginDirectory);

		if (!plugDirFile.exists()) {
			plugDirFile = new File(System.getProperty("user.dir"), pluginDirectory);
			logger.warn("Plug directory[" + pluginDirectory + "] not exist, use [" + plugDirFile.getAbsolutePath()
					+ "] instead.");
		}

		if (!plugDirFile.exists()) {
			logger.error("Cannot found plug directory. NOT load plugin.");
			return null;
		}

		return plugDirFile;
	}

	private File calcPluginUnitsDirectory() throws IOException {
		File plugUnitsDirFile = new File(Constants.PLUGIN_UNITS);

		if (!plugUnitsDirFile.exists()) {
			plugUnitsDirFile = new File(System.getProperty("user.dir"), Constants.PLUGIN_UNITS);
			logger.warn("Plug directory[" + Constants.PLUGIN_UNITS + "] not exist, use ["
					+ plugUnitsDirFile.getAbsolutePath() + "] instead.");
		}

		if (!plugUnitsDirFile.exists()) {
			FileUtils.forceMkdir(plugUnitsDirFile);
		}

		return plugUnitsDirFile;
	}

	/**
	 * 重新加载插件。
	 */
	protected void reloadCl() {
		File plugDirFile = calcPluginDirectory();
		if (plugDirFile == null || !plugDirFile.exists()) {
			logger.error("Plug in directroy[" + plugDirFile + "] not exist.");
			return;
		}

		URL[] jarUrls = getPluginDirectoryResource(plugDirFile);
		if (jarUrls == null || jarUrls.length == 0) {
			logger.info("Not found any plugin, directory[" + plugDirFile + "]");
			return;
		} else {
			JVMUtil.addAllJarsToClassPath(pluginDirectory);
		}

		// -- 清理插件配置目录.
		File pluginUnitsDir = null;
		try {
			pluginUnitsDir = calcPluginUnitsDirectory();
		} catch (IOException e) {
			logger.error("Cannot create plugin units directory[" + pluginUnitsDir + "].", e);
			return;
		}

		try {
			FileUtils.cleanDirectory(pluginUnitsDir);
		} catch (IOException e) {
			logger.error("Cannot clean plugin units directory[" + pluginUnitsDir + "].", e);
			return;
		}

		String[] resources = null;
		try {
			resources = PackageUtils.getResourceInPackage(Constants.JAR_PLUGIN_UNIT_CFG);
		} catch (IOException e) {
			logger.error("Cannot get resource[" + Constants.JAR_PLUGIN_UNIT_CFG + "] from classpath.", e);
			return;
		}

		for (String str : resources) {
			if (str.endsWith("cfg")) {
				int dotBegin = str.indexOf(".");
				String packageName = str.substring(0, dotBegin);
				String unitsName = str.substring(dotBegin + 1);
				InputStream in = Thread.currentThread().getContextClassLoader()
						.getResourceAsStream(packageName + "/" + unitsName);

				if (in != null) {
					File out = new File(pluginUnitsDir, unitsName);
					try {
						FileUtils.writeLines(out, IOUtils.readLines(in));
					} catch (IOException e) {
						logger.error("Write plugin unit cfg[" + str + "] fail.", e);
						continue;
					} finally {
						IOUtils.closeQuietly(in);
					}

					logger.debug("Write plugin unit cfg file[" + str + "]");
				}
			}

		}
	}

	private URL[] getPluginDirectoryResource(File plugDirFile) {
		List<URL> result = Lists.newArrayList();
		Collection<File> files = FileUtils.listFiles(plugDirFile, new String[] { "jar" }, true);
		for (File jarFile : files) {
			try {
				result.add(jarFile.toURI().toURL());
			} catch (MalformedURLException e) {
				logger.warn("Cannot get url with file[" + jarFile + "].", e);
			}
		}

		return result.toArray(new URL[result.size()]);
	}
}
